/*
 * Copyright 2014 ptma@163.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.joy.config;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Logger;
import org.joy.config.model.TemplateElement;
import org.joy.db.model.Column;
import org.joy.db.model.Table;
import org.joy.exception.ApplicationException;
import org.joy.generator.engine.EngineBuilder;
import org.joy.generator.engine.TemplateEngine;
import org.joy.generator.engine.TemplateEngineException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

public class Plan {

    private static final Logger   LOGGER             = Logger.getLogger(Plan.class);
    
    private String                classPath;
    private String                planPath;
    private List<Table> tables = new ArrayList<Table>();
    private static final String                FREEMARKER = "freemarker";
    private static final String                PLAN = "plan.ftl";
    private static final String DEFAULT_ENCODING = "UTF-8";
    

    public Plan(){
    	File f = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getFile());
        classPath = f.getParentFile().getPath() + File.separator;
        classPath = classPath.replaceAll("%20", " ");
        this.planPath= classPath +File.separator+"plan"+File.separator;
//        loadPlanFiles();
    }

    public void loadPlan(String filePath) throws ApplicationException {
        try {
        	
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = factory.newDocumentBuilder();
            Document doc = docBuilder.parse(filePath);

            XPathFactory f = XPathFactory.newInstance();
            XPath path = f.newXPath();

            Table table = new Table();
            this.tables.add(table);
            table.setTableName(path.evaluate("/plan/tableName/text()", doc));
            table.setTableType(path.evaluate("/plan/tableType/text()", doc));
            table.setTableAlias(path.evaluate("/plan/tableAlias/text()", doc));
            table.setRemarks(path.evaluate("/plan/remarks/text()", doc));
            table.setClassName(path.evaluate("/plan/className/text()", doc));
            table.setJavaProperty(path.evaluate("/plan/javaProperty/text()", doc));
            table.setCatalog(path.evaluate("/plan/catalog/text()", doc));
            table.setSchema(path.evaluate("/plan/schema/text()", doc));
            parseBaseColumns(doc, path, table);
            parsePrimaryKeys(doc, path, table);
            
            
            
        } catch (Exception e) {
            throw new ApplicationException(e);
        }
    }
    
    
    private void parsePrimaryKeys(Document doc, XPath path, Table table) throws XPathExpressionException {
        NodeList columnsList = (NodeList) path.evaluate("plan/primaryKeys/primaryKey", doc,
                                                         XPathConstants.NODESET);
        if (columnsList != null) {
            for (int i = 0; i < columnsList.getLength(); i++) {
            	table.addPrimaryKey(parseColumn(columnsList.item(i), path));
            }
        }
    }
    
    private void parseBaseColumns(Document doc, XPath path, Table table) throws XPathExpressionException {
        NodeList columnsList = (NodeList) path.evaluate("plan/baseColumns/baseColumn", doc,
                                                         XPathConstants.NODESET);
        if (columnsList != null) {
            for (int i = 0; i < columnsList.getLength(); i++) {
            	table.addBaseColumn(parseColumn(columnsList.item(i), path));
            }
        }
    }

    private Column parseColumn(Node node, XPath path) throws XPathExpressionException {
    	String name = path.evaluate("@columnName", node);
    	Column column = new Column(name);
    	column.setJdbcType(Integer.parseInt(path.evaluate("@jdbcType", node)));
    	
    	column.setPrimaryKey(Boolean.parseBoolean(path.evaluate("./primaryKey/text()", node)));
    	column.setForeignKey(Boolean.parseBoolean(path.evaluate("./foreignKey/text()", node)));
    	column.setSize(Integer.parseInt(path.evaluate("./size/text()", node)));
    	column.setDecimalDigits(Integer.parseInt(path.evaluate("./decimalDigits/text()", node)));
    	column.setNullable(Boolean.parseBoolean(path.evaluate("./nullable/text()", node)));
    	column.setUnique(Boolean.parseBoolean(path.evaluate("./unique/text()", node)));
    	column.setIndexed(Boolean.parseBoolean(path.evaluate("./indexed/text()", node)));
    	column.setAutoincrement(Boolean.parseBoolean(path.evaluate("./autoincrement/text()", node)));
    	column.setDefaultValue(path.evaluate("./defaultValue/text()", node));
    	column.setRemarks(path.evaluate("./remarks/text()", node));
    	column.setJdbcTypeName(path.evaluate("./jdbcTypeName/text()", node));
    	column.setJavaProperty(path.evaluate("./javaProperty/text()", node));
    	column.setJavaType(path.evaluate("./javaType/text()", node));
    	column.setFullJavaType(path.evaluate("./fullJavaType/text()", node));
    	column.setEditor(path.evaluate("./editor/text()", node));
    	column.setDisplay(Boolean.parseBoolean(path.evaluate("./display/text()", node)));
    	column.setSearchable(Boolean.parseBoolean(path.evaluate("./searchable/text()", node)));
    	column.setDict(path.evaluate("./dict/text()", node));
    	column.setUi(path.evaluate("./ui/text()", node));
    	column.setFullRow(Boolean.parseBoolean(path.evaluate("./fullRow/text()", node)));
    	column.setRequire(Boolean.parseBoolean(path.evaluate("./require/text()", node)));
    	column.setEditable(Boolean.parseBoolean(path.evaluate("./editable/text()", node)));

        return column;

       
    }
    
	public void loadPlanFiles() {
		this.tables = new ArrayList<Table>();
		/*
		File file = new File(planPath);
		File[] tempList = file.listFiles();
		System.out.println("该目录下对象个数：" + tempList.length);
		for (int i = 0; i < tempList.length; i++) {
			if (tempList[i].isFile()) {
				try {
					loadPlan(tempList[i].getAbsolutePath());
				} catch (ApplicationException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("文     件：" + tempList[i].getAbsolutePath());
			}
		}
		*/
		String path=classPath+"plan.json";
		String planString = readFile(path);
		Type type = new TypeToken<ArrayList<Table>>() {}.getType();  
		Gson gson = new Gson();
		this.tables = gson.fromJson(planString, type); 

	}


    public void save(Map<String, Object> model) {
    	try {
    		savePlan4Json(model);
	    	TemplateElement templateElement = new TemplateElement("plan", FREEMARKER, PLAN, planPath, "${table.tableName}.xml", DEFAULT_ENCODING);
	    	EngineBuilder engineBuilder = new EngineBuilder(classPath);
	    	TemplateEngine templateEngine = engineBuilder.getTemplateEngine(FREEMARKER);
			templateEngine.processToFile(model, templateElement);
		} catch (TemplateEngineException e) {
			// TODO Auto-generated catch block
			LOGGER.info(e.getMessage(), e);
		}
    }
    
    public void savePlan4Json(Map<String, Object> model) {
        try
        {
    	loadPlanFiles();
    	Table modelTable = (Table) model.get("table");
    	Boolean containce = false;
    	for(Table table:tables){
    		if(table.getTableName().equals(modelTable.getTableName())&&table.getCatalog().equals(modelTable.getCatalog())){
    			tables.remove(table);
    			tables.add(0,modelTable);
    			containce = true;
    			break;
    		}
    	}
    	if(!containce){
    		tables.add(modelTable);
    	}
    	
    	String path=classPath+"plan.json";
    	writeFile(path);
        }
        catch(Exception e)
        {
        	LOGGER.info(e.getMessage(), e);
        }
    		
    }
    
    public void deletePlan4Json(Map<String, Object> model) {
        try
        {
    	loadPlanFiles();
    	Table modelTable = (Table) model.get("table");
    	for(Table table:tables){
    		if(table.getTableName().equals(modelTable.getTableName())&&table.getCatalog().equals(modelTable.getCatalog())){
    			tables.remove(table);
    			break;
    		}
    	}
    	
    	String path=classPath+"plan.json";
    	writeFile(path);
        }
        catch(Exception e)
        {
        	LOGGER.info(e.getMessage(), e);
        }
    		
    }
    
    public void  writeFile(String path)
    {
	    Gson gson = new Gson();
		String tableString = gson.toJson(this.tables);
	    LOGGER.info("---->model convert jsonStr:" + tableString);
	    try {
	    File file=new File(path);
	    if(!file.exists())
	        file.createNewFile();
	    FileOutputStream out;
		
			out = new FileOutputStream(file,false);
		 //如果追加方式用true        
	    StringBuffer sb=new StringBuffer(tableString);
	    out.write(sb.toString().getBytes("utf-8"));//注意需要转换对应的字符集
	    out.close();
	    } catch (Exception e) {
	    	LOGGER.info(e.getMessage(), e);
		}
    }
    
    public String readFile(String path)
    {
        StringBuffer sb=new StringBuffer();
        String tempstr=null;
        try
        {
            File file=new File(path);
            if(!file.exists())
                throw new FileNotFoundException();            
            FileInputStream fis=new FileInputStream(file);
            BufferedReader br=new BufferedReader(new InputStreamReader(fis));
            while((tempstr=br.readLine())!=null)
                sb.append(tempstr);
        }
        catch(IOException e)
        {
        	LOGGER.info(e.getMessage(), e);
        }
        return sb.toString();
    }


	public List<Table> getTables() {
		return tables;
	}

	public void setTables(List<Table> tables) {
		this.tables = tables;
	}
}
